// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;

namespace SnapObjects.Data.Odbc
{
    internal class OdbcDeleteBuilderParser : DeleteBuilderParser
    {
        public OdbcDeleteBuilderParser(
            ISqlStatementFactory factory,
            ISqlGenerationHelper helper,
            ITypeMapper typeMapper) : base(factory, helper, typeMapper)
        {
        }

        protected override IEnumerable<SqlToken> ParseTokens(string rawString)
        {
            using (ISqlTokenizer tokenizer = 
                ((IOdbcSqlStatementFactory)_factory).GetSqlTokenizer(rawString, _helper))
            {
                return tokenizer.ToList();
            }
        }

        protected override IEnumerable<SqlToken> ParseTableElement(
            SqlBuildStatement statement, TableElement table)
        {
            var tokens = new List<SqlToken>();

            if (table.IsCross)
            {
                tokens.Add(new SqlCharacterToken(SqlCharacters.Comma));
            }

            switch (table.TableType)
            {
                case SqlTableType.Clause:
                {
                    tokens.AddRange(this.ParseRawString(statement, table.Table));
                }
                break;
                case SqlTableType.TableName:
                {
                    this.GetTableInfo(statement, table, out var schema, out var tableName);

                    if (!String.IsNullOrWhiteSpace(schema))
                    {
                        tokens.Add(new SqlIdentifierToken(schema));
                        tokens.Add(new SqlCharacterToken(SqlCharacters.Period));
                    }
                    tokens.Add(new SqlIdentifierToken(tableName));
                }
                break;
                case SqlTableType.Statement:
                {
                    tokens.Add(new SqlCharacterToken(SqlCharacters.OpenParentheses));
                    tokens.Add(this.ParseStatement(table.Statement));
                    tokens.Add(new SqlCharacterToken(SqlCharacters.CloseParentheses));
                }
                break;
            }

            if (!String.IsNullOrWhiteSpace(table.Alias))
            {
                this.ParseTableAlias(tokens, table.Alias);
            }

            return tokens;
        }

        private void GetTableInfo(SqlBuildStatement statement, TableElement table,
            out string schema, out string tableName)
        {
            schema = table.Schema;
            tableName = table.Table;

            if (String.IsNullOrWhiteSpace(table.Schema))
            {
                this.FindTableInFrom(((DeleteBuildStatement)statement).From, table,
                    ref schema, ref tableName);
                if (String.IsNullOrWhiteSpace(schema))
                {
                    this.FindTableInJoin(((DeleteBuildStatement)statement).Join, table,
                        ref schema, ref tableName);
                }
            }
        }

        private void FindTableInFrom(SqlBuildClause clause, TableElement table,
            ref string schema, ref string tableName)
        {
            foreach (var element in clause)
            {
                if (table.ElementName.Equals(element.ElementName,
                    StringComparison.OrdinalIgnoreCase)
                    && element.ElementType == SqlElementType.Table)
                {
                    if (((TableElement)element).TableType == SqlTableType.TableName)
                    {
                        schema = ((TableElement)element).Schema;
                        tableName = ((TableElement)element).Table;

                        break;
                    }
                }
            }
        }

        private void FindTableInJoin(SqlBuildClause clause, TableElement table,
            ref string schema, ref string tableName)
        {
            foreach (var element in clause)
            {
                if (table.ElementName.Equals(element.ElementName,
                    StringComparison.OrdinalIgnoreCase)
                    && element.ElementType == SqlElementType.Join)
                {
                    if (((JoinElement)element).TableType == SqlTableType.TableName)
                    {
                        schema = ((JoinElement)element).Schema;
                        tableName = ((JoinElement)element).Table;

                        break;
                    }
                }
            }
        }
    }
}
